package domain;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;

import util.ContactsContainer;
import util.DAOException;
import util.VersionException;
import domain.factory.POJOFactory;
import domain.itf.IDAOContact;

public class DAOContact extends AbstractDAO implements IDAOContact {

	private static final Logger logger = Logger.getLogger(DAOContact.class);

	public DAOContact() {}
	
	
	public Contact getById(Integer id) throws DAOException {
		try {
			return (Contact) super.getHibernateTemplate().get(Contact.class, id);
		}
		catch(DataAccessException e) {
			e.printStackTrace();
			throw new DAOException("Impossible to get the Contact. getById(" + id + "). " + e.getMessage());
		}
	}

	
	public ContactsContainer getAllContacts() throws DAOException {
		try {
			return (ContactsContainer) getHibernateTemplate().execute(new HibernateCallback() {
		        
		        public ContactsContainer doInHibernate(Session session) throws HibernateException, SQLException {
		        	String requestString = "from Contact contact";
					Query request = session.createQuery(requestString);
					
					// 2nd level cache
					request.setCacheable(true);
					
					List<?> results = request.list();
					ContactsContainer contactContainer = new ContactsContainer();
					for (Object result : results) {
						if (result instanceof Contact)
							contactContainer.add((Contact) result);
						else
							logger.error("getAllContactsDB : The request give another object than a contact.");
					}
					return contactContainer;
		        }
		    });
		} catch (DataAccessException e) {
			e.printStackTrace();
			throw new DAOException("Impossible to get all contacts. " + e.getMessage());
		}
	}

	
	public Contact addContact(String firstname, String lastname, String emailC, Address address, 
			Set<ContactGroup> books, Set<PhoneNumber> phones, Integer numSiret) 
					throws DAOException {
		Contact contact;
		if (numSiret != null)
			contact = POJOFactory.instance.newEntreprise(firstname, lastname, emailC, address, books, phones, numSiret);
		else
			contact = POJOFactory.instance.newContact(firstname, lastname, emailC, address, books, phones);
		
		return this.addContact(contact);
	}

	
	public Contact addContact(Contact contact) throws DAOException {
		try{
			Serializable newID = super.getHibernateTemplate().save(contact);
			if(newID instanceof Integer)
				contact.setId((Integer) newID);
			else //We have to stay in the if bloc, all ids are Integer
				throw new DAOException("Impossible to get the new ID of the Contact saved.");
			return contact;
		} catch(DataAccessException e) {
			e.printStackTrace();
			throw new DAOException("Impossible to create the Contact. " + e.getMessage());
		}
	}

	
	public Contact updateContact(int id, long version, String firstname, String lastname,
			String emailC, Address address, Set<ContactGroup> books, Set<PhoneNumber> phones, Integer numSiret) 
					 throws DAOException, VersionException {
		Contact contact;
		if (numSiret != null)
			contact = POJOFactory.instance.newEntreprise(firstname, lastname, emailC, address, books, phones, numSiret);
		else
			contact = POJOFactory.instance.newContact(firstname, lastname, emailC, address, books, phones);
		contact.setId(id);
		contact.setVersion(version);
		return this.updateContact(contact);
	}

	
	public Contact updateContact(Contact contact) throws DAOException, VersionException {
		// versionning verification
		long version = super.getVersionDB(Contact.class, contact.getId());
		if (version != contact.getVersion())
			throw new VersionException();
		
		try {
			// update the version before save the adress modification
			contact.setVersion(contact.getVersion() + 1);
			super.getHibernateTemplate().update(contact);
			return contact;
			
		} catch(DataAccessException e) {
			e.printStackTrace();
			throw new DAOException("Impossible to update the Contact where the id is " + contact.getId() + " . " + e.getMessage());
		}
	}

	
	public boolean deleteContact(int memberId) throws DAOException {
		try {
			Object contactO = this.getById(memberId);
			if(contactO != null && contactO instanceof Contact) {
				super.getHibernateTemplate().delete(contactO);
			}
			return true;
		} catch (DataAccessException e) {
			e.printStackTrace();
			throw new DAOException("Impossible to delete the address. " + e.getMessage());
		}
	}
	
	
	public ContactsContainer searchContactsWithMultipleCriteria(final String firstName, final String lastName, 
			final String email, final boolean isAscendant, final String order) throws DAOException {
		try {
			return (ContactsContainer) getHibernateTemplate().execute(new HibernateCallback() {
		        
		        public ContactsContainer doInHibernate(Session session) throws HibernateException, SQLException {

					Order orderBy;
					if (isAscendant)
						orderBy = Order.asc(order);
					else
						orderBy = Order.desc(order);
					
					List<?> results = session.createCriteria(Contact.class)
							.add(Restrictions.like("firstName", "%"+firstName+"%"))
							.add(Restrictions.like("lastName", "%"+lastName+"%"))
							.add(Restrictions.like("email", "%"+email+"%"))
							.addOrder(orderBy).setCacheable(true).list();
					ContactsContainer contactContainer = new ContactsContainer();
					for (Object result : results) {
						if (result instanceof Contact)
							contactContainer.add((Contact) result);
						else
							System.err.println("Pas normal, le résultat n'est pas un contact??");
					}
					return contactContainer;
		        }
		    });
		} catch (DataAccessException e) {
			e.printStackTrace();
			throw new DAOException("Impossible to search the contacts with multiple criteria. " + e.getMessage());
		}
	}

	
	public ContactsContainer getByGroups(final Integer[] groups) throws DAOException {
		try {
			return (ContactsContainer) getHibernateTemplate().execute(new HibernateCallback() {
		        
		        public ContactsContainer doInHibernate(Session session) throws HibernateException, SQLException {

					List<?> results = session.createCriteria(Contact.class)
							.createAlias("books", "book")
							.add(Restrictions.in("book.groupId", groups)).list();
					ContactsContainer contactContainer = new ContactsContainer();
					for (Object result : results) {
						if (result instanceof Contact)
							contactContainer.add((Contact) result);
						else
							System.err.println("Pas normal, le résultat n'est pas un contact??");
					}
					return contactContainer;
		        }
		    });
		} catch (DataAccessException e) {
			e.printStackTrace();
			throw new DAOException("Impossible to search the contacts by groups. " + e.getMessage());
		}
	}
}
